package p2p;

import java.net.DatagramPacket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/** @author ei08150
 */

/* Diagrama que representa o aspecto de um chunk de ficheiro quando
 * este esta na rede
  0       7 8       63 64         248      256
  +--------+---...----+----...---+--------+
  |             file ID/sha               |
  +------------...---------...------------+
  |    chunkNumber    |        Reserved   |
  +------------...---------...------------+
  |                 Data                  |
  +.......................................+
  |                 Data                  |
  +.......................................+
  |                 Data                  |
  +.......................................+
  |                 Data                  |
  +.......................................+
*/


public class NetworkChunk {
	/** Buffer that will hold the package sent to the network */
    private byte[] buffer = null;

	private final int hashSize = 32;
	private final int chunkNumberSize = 8;
	private final int reservedSize = 24;
		

    /** Create a NetworkChunk whose buffer has the given parameters
     * @param hash The requested hash
     * @param chunkNumber The chunk to which the data belongs
     * @param data The chunk itself (the part of the file sent)
     */ 
    public NetworkChunk(String hash, int chunkNumber, byte[] data) {
        buffer = new byte[hashSize + chunkNumberSize + reservedSize + data.length];

        byte[] hashInByte = Global.hexToByteCheckSum(hash);
		
        // A Java function WITHOUT CamelCase?! The mind boggles. - ei08150
        System.arraycopy(hashInByte, 0, buffer, 0, hashInByte.length);

		// Use Little-endian of chunk number
		ByteBuffer bb = ByteBuffer.allocate(chunkNumberSize);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putLong(chunkNumber);
		bb.rewind();
        byte[] chunkInByte = new byte[chunkNumberSize];
		bb.get(chunkInByte, 0, chunkNumberSize);

        System.arraycopy(chunkInByte, 0, buffer, hashSize, chunkInByte.length);

        System.arraycopy(data, 0, buffer, hashSize + chunkNumberSize + reservedSize, data.length);
    }

    /** Create a NetworkChunk directly from a buffer
     * @param buf The buffer, as retrieved from the method NetwordChunk.getBuffer()
     */
    public NetworkChunk(byte[] buf) {
        buffer = buf;
    }

	/** Create a NetworkChunk from a DatagramPacket received from the data socket
	 * @param packet The DatagramPacket as received from the data socket
	 */
	public NetworkChunk(DatagramPacket packet) {
		buffer = packet.getData();
	}

    /** Get the whole buffer of this NetworkChunk
     * @return The buffer as a byte[]
     */
    public byte[] getBuffer() {
        return buffer;
    }

    /** Get the hash of this NetworkChunk
     * @return The hash as a String
     */
    public String getHash() {
        byte[] hashInByte = new byte[hashSize];

        System.arraycopy(buffer, 0, hashInByte, 0, hashSize);

        // Convert byte[] to String
        return Global.byteToHexCheckSum(hashInByte);
    }

    /** Get the number of the chunk of this NetworkChunk
     * @return The number of the chunk as an integer
     */ 
    public int getChunkNumber() {
        byte[] chunkInByte = new byte[chunkNumberSize];

        System.arraycopy(buffer, hashSize, chunkInByte, 0, chunkNumberSize);

		// Use Little-endian of chunk number
		ByteBuffer bb = ByteBuffer.wrap(chunkInByte);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		long number = bb.getLong();
        
        return (int)number;
    }

    /** Get the data sent on this NetworkChunk
     * @return The data as a byte[]
     */
    public byte[] getData() {
		int size = buffer.length - (chunkNumberSize + hashSize + reservedSize);
        byte[] data = new byte[size];

        System.arraycopy(buffer, chunkNumberSize + hashSize + reservedSize, data, 0, size);

        return data;
    }
}
